View Javadoc

1   /*
2    * Copyright (C) 1998-2000 Semiotek Inc.  All Rights Reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted under the terms of either of the following
6    * Open Source licenses:
7    *
8    * The GNU General Public License, version 2, or any later version, as
9    * published by the Free Software Foundation
10   * (http://www.fsf.org/copyleft/gpl.html);
11   *
12   *  or
13   *
14   * The Semiotek Public License (http://webmacro.org/LICENSE.)
15   *
16   * This software is provided "as is", with NO WARRANTY, not even the
17   * implied warranties of fitness to purpose, or merchantability. You
18   * assume all risks and liabilities associated with its use.
19   *
20   * See www.webmacro.org for more information on the WebMacro project.
21   */
22  
23  package org.webmacro.resource;
24  
25  import org.webmacro.Broker;
26  import org.webmacro.TemplateException;
27  import org.webmacro.WMConstants;
28  import org.webmacro.engine.WMTemplate;
29  import org.webmacro.servlet.LocaleTool;
30  import org.webmacro.util.NativeAsciiReader;
31  
32  import java.io.*;
33  import java.net.URL;
34  import java.util.HashMap;
35  import java.util.Locale;
36  import java.util.Properties;
37  
38  /***
39   * FileTemplate objects read their template data from a text file.
40   */
41  
42  public class URLTemplate extends WMTemplate
43  {
44  
45      /***
46       * CVS revision
47       */
48  
49      public static final String RCS = "@(#) $Id: URLTemplate.java,v 1.11 2005/10/31 02:46:41 lanesharman Exp $";
50  
51      /***
52       * The location of the resourse (file) this template was read from.
53       */
54  
55      private final URL _url;
56  
57      /***
58       * The physical file referred to by "file:" and "jar:" URLs
59       */
60  
61      private File underLyingFile = null;
62  
63      /***
64       * The time the underlying file was last modified
65       */
66  
67      private long underLyingFileLastModTime = 0;
68  
69      /***
70       * Cache for any per-directory encoding files
71       */
72  
73      private final HashMap propertiesCache = new HashMap();
74  
75      private String _inputEncoding = null;
76      private String _outputEncoding = null;
77      private Locale _outputLocale = null;
78  
79      /***
80       * A dummy object used as a place holder in the encoding cache
81       */
82  
83      private static final Object dummy = new Object();
84  
85      /***
86       * Instantiate a template based on the specified file.
87       * We use can use the special case or URLs like
88       * <pre>
89       *        file:xxxxxx
90       * or
91       *        jar:xxxxxx!yyyyyy
92       *</pre>
93       * extracting the xxxxxx.  In the latter case the reference is to the jar containing the
94       * template.
95       */
96  
97      public URLTemplate (
98              Broker broker,
99              URL templateURL
100             )
101     {
102         super(broker);
103         _url = templateURL;
104         String _u = _url.toExternalForm();
105 
106         _log.debug("URLTemplate: " + _u);
107 
108         if (_u.startsWith("jar:"))
109         {
110             int p = _u.indexOf("!");
111             _u = _u.substring(4, p);
112         }
113 
114         if (_u.startsWith("file:"))
115         {
116             underLyingFile = new File(_u.substring(5));
117             underLyingFileLastModTime = underLyingFile.lastModified();
118         }
119 
120         setupLocalProperties();
121     }
122 
123     /***
124      * URL based templates are difficult to detect as changed in general, but the two
125      * special cases can be determined for reloading.
126      * <pre>
127      * file:[path] and 
128      * jar:file:[jarpath]![path]
129      * </pre>
130      * Imply a reference to the actual file.
131      */
132 
133     public boolean shouldReload ()
134     {
135         if (underLyingFile == null) return false;
136         long lastMod = underLyingFile.lastModified();
137 
138         return ((lastMod > 0) && (lastMod > underLyingFileLastModTime));
139     }
140 
141 
142     /***
143      * Look for TemplateEncoding in a file WebMacro.local in the same
144      * directory as the template
145      *
146      * TODO - should make the encoding cache a bit smarter- doesn't detect
147      * if the file changes
148      */
149 
150     private void setupLocalProperties ()
151     {
152         InputStream is = null;
153         URL u = null;
154 
155         try
156         {
157 
158             u = new URL(_url, WMConstants.WEBMACRO_LOCAL_FILE);
159             _log.debug("Looking for encodings file: " + u);
160             Object obj = propertiesCache.get(u);
161 
162             if (obj != null)
163             {
164                 return;
165             }
166 
167             Properties p = new Properties();
168             is = u.openStream();
169             p.load(is);
170             _inputEncoding = p.getProperty(WMConstants.TEMPLATE_INPUT_ENCODING);
171             _outputEncoding = p.getProperty(WMConstants.TEMPLATE_OUTPUT_ENCODING);
172 
173             String loc = p.getProperty(WMConstants.TEMPLATE_LOCALE);
174             if (loc != null)
175             {
176                 _outputLocale = LocaleTool.buildLocale(loc);
177             }
178 
179             propertiesCache.put(u, dummy);
180 
181 
182         }
183         catch (Exception e)
184         {
185             // if there's an Exception here, put a dummy object here so
186             // we don't try to look for the file every time
187             //
188             propertiesCache.put(u, dummy);
189         }
190         finally
191         {
192             if (_inputEncoding == null)
193             {
194                 _inputEncoding = getDefaultEncoding();
195             }
196 
197             if (is != null)
198             {
199                 try
200                 {
201                     is.close();
202                 }
203                 catch (Exception ignore)
204                 {
205                 }
206             }
207         }
208     }
209 
210     /***
211      * Get the stream the template should be read from. Parse will
212      * call this method in order to locate a stream.
213      *
214      */
215     protected Reader getReader () throws IOException
216     {
217         _log.debug("Using encoding " + _inputEncoding);
218 
219         if (_inputEncoding.equals("native_ascii"))
220         {
221             return new NativeAsciiReader(
222                     new InputStreamReader(_url.openStream(), "ASCII"));
223         }
224         else
225         {
226             return new BufferedReader(
227                     new InputStreamReader(_url.openStream(), _inputEncoding));
228         }
229     }
230 
231     /***
232      * return the URL for the current template.
233      */
234     public URL getURL ()
235     {
236         return _url;
237     }
238 
239     /***
240      * Return a name for this template. For example, if the template reads
241      * from a file you might want to mention which it is--will be used to
242      * produce error messages describing which template had a problem.
243      */
244     public String toString ()
245     {
246         return "URLTemplate:" + _url;
247     }
248 
249     public void parse () throws IOException, TemplateException
250     {
251         super.parse();
252         if ((_outputEncoding != null)
253                 && (getParam(WMConstants.TEMPLATE_OUTPUT_ENCODING) == null))
254         {
255             _log.debug("Setting output encoding to " + _outputEncoding);
256             setParam(WMConstants.TEMPLATE_OUTPUT_ENCODING, _outputEncoding);
257         }
258         if ((_outputLocale != null)
259                 && (getParam(WMConstants.TEMPLATE_LOCALE) == null))
260         {
261             _log.debug("Setting output locale to " + _outputLocale);
262             setParam(WMConstants.TEMPLATE_LOCALE, _outputLocale);
263         }
264     }
265 }